
if (!Array.prototype.add) {
    Array.prototype.add = function(item) {
        this[this.length] = item ;
    }
}

/*******************************************************************************
* Ruler
*******************************************************************************/
Mappl.Tool.Ruler = Ext.extend( Mappl.Tool.Base, {

    afterConstruction : function() {
        this.flCanDrag    = false ;
        this.flDragFrame  = false ;
        this.flEscape     = true ;
        this.polyLine = Mappl.Map.Ruler.PolyLineView.classFactory.create({});
        this.polyLine.renderTo(this.runtime.divMappl);
    }

    , onAction : function() {
        this.beforeAction();
        var point = Mappl.Map.Ruler.PointNodeView.classFactory.create({
            x : this.runtime.toolX,
            y : this.runtime.toolY
        });
        this.polyLine.getCtrl().addPoint(point);
        //point.renderTo(Ext.get(this.runtime.activeImgMap).dom.parentNode);
    }

    , returnFalse : function() {
        return false ;
    }

    , onDragStart : function() {
    }

    , onDragStop : function() {
    }

    , getToolAliasOnAltAction : function() {
        return null ;
    }

    , getToolAliasOnAltDragStart : function() {
        return null ;
    }

    , getToolAliasOnCtrlDragStart : function() {
        return null ;
    }

    , beforeActivation : function() {

    }

    , beforeDeactivation : function() {

    }

} );

Ext.namespace("Mappl.Map.Ruler");

Mappl.Map.Ruler.PointNode = function(aConfig) {
    Mappl.Map.Ruler.PointNode.superclass.constructor.call(aConfig);
    this.attributes = aConfig ;
    this.x = this.attributes.x ;
    this.y = this.attributes.y ;
    this.bHover = false ;
    this.bFocus = false ;
    this.bDrag = false ;
    this.addEvents(
        "hoverIn",
        "hoverOut",
        "focus",
        "blur",
        "dragstart",
        "dragstop",
        "dragcancel",
        "stateChanged",
        "destroy"
    );
    return this ;
};
Ext.extend(Mappl.Map.Ruler.PointNode, Ext.util.Observable, {
    getX : function() { return this.x ; },
    setX : function(x) { this.x = x ; },
    getY : function() { return this.y ; },
    setY : function(y) { this.y = y ; },
    getHover : function() { return this.bHover ; },
    setHover : function(bHover) { this.bHover = bHover ; },
    getFocus : function() { return this.bFocus ; },
    setFocus : function(bFocus) { this.bFocus = bFocus ; },
    getDrag : function() { return this.bDrag ; },
    setDrag : function(bDrag) { this.bDrag = bDrag ; },
    storeState : function() {
        this._store = {
            "x" : this.getX(),
            "y" : this.getY()
        };
    },
    restoreState : function() {
        this.setX(this._store.x);
        this.setY(this._store.y);
    }
});
Mappl.Map.Ruler.PointNode.classFactory = new Mappl.ClassFactory(Mappl.Map.Ruler.PointNode, "pointNode");

Mappl.Map.Ruler.PointNodeCtrl = function(aConfig) {
    this.model = Mappl.Map.Ruler.PointNode.classFactory.get(aConfig);
    this.model.on("destroy", this.eventFn_destroy, this);
    return this ;
}
Mappl.Map.Ruler.PointNodeCtrl.classFactory = new Mappl.ClassFactory(Mappl.Map.Ruler.PointNodeCtrl, "pointNodeCtrl");
Mappl.Map.Ruler.PointNodeCtrl.prototype = {
    getModel : function() { return this.model ; },
    mouseIn : function(e) {
        with (this.getModel()) {
            setHover(true);
            fireEvent("hoverIn", this.getModel());
        }
    },
    mouseOut : function(e) {
        with (this.getModel()) {
            setHover(false);
            fireEvent("hoverOut", this.getModel());
        }
    },
    focus : function(e) {
        with (this.getModel()) {
            setFocus(true);
            fireEvent("focus", this.getModel());
        }

    },
    blur : function(e) {
        with (this.getModel()) {
            setFocus(false);
            fireEvent("blur", this.getModel());
        }
    },
    startDrag : function(e) {
        with (this.getModel()) {
            storeState();
            setDrag(true);
            fireEvent("dragstart", this.getModel());
        }
    },
    cancelDrag : function(e) {
        with (this.getModel()) {
            restoreState();
            setDrag(false);
            fireEvent("dragcancel", this.getModel());
            fireEvent("stateChanged", this.getModel());
        }
    },
    stopDrag : function(e) {
        with (this.getModel()) {
            setDrag(false);
            fireEvent("dragstop", this.getModel());
        }
    },
    moveBy : function(dx,dy) {
        with (this.getModel()) {
            setX(getX() + dx);
            setY(getY() + dy);
            fireEvent("stateChanged", this.getModel());
        }
    },
    destroy : function(e) {
        this.getModel().fireEvent("destroy", this.getModel());
    },
    eventFn_destroy : function(e) {
        this.model.un("destroy", this.eventFn_destroy, this);
        delete this.model ;
    }
}

Mappl.Map.Ruler.PointNodeView = function(aConfig) {
    if ("model" in aConfig && "ctrl" in aConfig) {
        this.model = Mappl.Map.Ruler.PointNode.classFactory.get(aConfig.model);
        this.ctrl = Mappl.Map.Ruler.PointNodeCtrl.classFactory.get(aConfig.ctrl);
    } else {
        this.model = Mappl.Map.Ruler.PointNode.classFactory.get(aConfig);
        this.ctrl = Mappl.Map.Ruler.PointNodeCtrl.classFactory.get(this.model); 
    }
    //console.dir(this.ctrl);
    this.dom = null ;
    this.getModel().on("destroy", this.eventFn_destroy, this);
    this.getModel().on("stateChanged", this.eventFn_stateChanged, this);
    return this ;
}
Mappl.Map.Ruler.PointNodeView.classFactory = new Mappl.ClassFactory(Mappl.Map.Ruler.PointNodeView, "pointNodeView");
Mappl.Map.Ruler.PointNodeView.prototype = {
    getModel : function() { return this.model ; },
    getCtrl : function() { return this.ctrl ; },
    getDom : function() {
        if (this.dom == null) this.build();
        return this.dom ;
    },
    build : function() {
        this.dom = e$('a id="point' + Ext.id() + '" href="javascript:void(0)" class="mappl-map-ruler-point"')
                .css$({
            top : this.getModel().getY(),
            left : this.getModel().getX() 
        }).ae$(e$('img src="' + Ext.BLANK_IMAGE_URL + '" border="0"').css$({ width : "100%", height : "100%"}));
    },
    // Attach events after added to dom
    attach : function() {
        with (Ext.get(this.dom)) {
            on('mouseover', Mappl.Map.Ruler.PointNodeView.eventFn_mouseOver, this);
            on('mouseout', Mappl.Map.Ruler.PointNodeView.eventFn_mouseOut, this);
            on('focus', Mappl.Map.Ruler.PointNodeView.eventFn_focus, this);
            on('blur', Mappl.Map.Ruler.PointNodeView.eventFn_blur, this);
            on('dblclick', Mappl.Map.Ruler.PointNodeView.eventFn_dblClick, this);
            on('mousedown', Mappl.Map.Ruler.PointNodeView.eventFn_mousedown, this);
            on('mouseup', Mappl.Map.Ruler.PointNodeView.eventFn_mouseup, this);
            on('click', function(e) { return false ; }, this, { stopEvent : true });
        }
    },
    // Detach events before remove from dom
    detach : function() {
        Ext.get(this.dom).removeAllListeners();
    },
    renderTo : function(renderToElement) {
        this._removeChild();
        renderToElement.appendChild(this.getDom());
        this.attach();
    },
    _removeChild : function() {
        if (this.getDom().parentNode) {
            this.detach();
            this.getDom().parentNode.removeChild(this.dom);
        }
    },
    eventFn_destroy : function() {
        this._removeChild();
        delete this.dom ;
        delete this.model ;
        delete this.ctrl ;
    },
    eventFn_stateChanged : function() {
        this.dom.css$({
            top : this.getModel().getY(),
            left : this.getModel().getX() 
        });
    }
}
Mappl.Map.Ruler.PointNodeView.eventFn_mouseOver = function(e) {
    //alert("this.getCtrl() [" + this.getCtrl() + "]");
    this.getCtrl().mouseIn(e);
}
Mappl.Map.Ruler.PointNodeView.eventFn_mouseOut = function(e) {
    this.getCtrl().mouseOut(e);
}
Mappl.Map.Ruler.PointNodeView.eventFn_focus = function(e) {
    this.getCtrl().focus(e);
}
Mappl.Map.Ruler.PointNodeView.eventFn_blur = function(e) {
    this.getCtrl().blur(e);
}
Mappl.Map.Ruler.PointNodeView.eventFn_dblClick = function(e) {
    this.getCtrl().destroy();
}
Mappl.Map.Ruler.PointNodeView.eventFn_mousedown = function(e) {
    e.stopEvent();
    this.getCtrl().startDrag(e);
    this.dragPosition = {
        "x" : e.getPageX(),
        "y" : e.getPageY()
    };
    Ext.get(this.dom.parentNode).on("mousemove", Mappl.Map.Ruler.PointNodeView.eventFn_dragging, this);
    Ext.get(this.dom.parentNode).on("mouseup", Mappl.Map.Ruler.PointNodeView.eventFn_mouseup, this);
    Ext.getDoc().on("keydown", Mappl.Map.Ruler.PointNodeView.eventFn_handleESC, this);
}
Mappl.Map.Ruler.PointNodeView.eventFn_dragging = function(e) {
    var currentPosition = {
        "x" : e.getPageX(),
        "y" : e.getPageY()
    };
    this.getCtrl().moveBy(
        currentPosition.x - this.dragPosition.x,
        currentPosition.y - this.dragPosition.y
    );
    this.dragPosition = currentPosition ;
}
Mappl.Map.Ruler.PointNodeView.eventFn_mouseup = function(e) {
    e.stopEvent();
    Ext.get(this.dom.parentNode).un("mouseup", Mappl.Map.Ruler.PointNodeView.eventFn_mouseup, this);
    Ext.get(this.dom.parentNode).un("mousemove", Mappl.Map.Ruler.PointNodeView.eventFn_dragging, this);
    Ext.getDoc().un("keydown", Mappl.Map.Ruler.PointNodeView.eventFn_handleESC, this);
    this.getCtrl().stopDrag(e);
}
Mappl.Map.Ruler.PointNodeView.eventFn_handleESC = function(e) {
    if (e.getKey() == e.ESC) {
        Ext.get(this.dom.parentNode).un("mouseup", Mappl.Map.Ruler.PointNodeView.eventFn_mouseup, this);
        Ext.get(this.dom.parentNode).un("mousemove", Mappl.Map.Ruler.PointNodeView.eventFn_dragging, this);
        Ext.getDoc().un("keydown", Mappl.Map.Ruler.PointNodeView.eventFn_handleESC, this);
        this.getCtrl().cancelDrag(e);
    }
}

// PolyLine
Mappl.Map.Ruler.PolyLine = function(aConfig) {
    Mappl.Map.Ruler.PolyLine.superclass.constructor.call(aConfig);
    this.attributes = aConfig ;
    this.points = new Array();
    if (this.attributes.items) {
        for (var i = 0, l = this.attributes.items.length ; i < l ; i ++) {
            var item = this.attributes.items[i] ;
            this.addPoint(item);
        }
    }
    this.addEvents(
        "afterAddPoint",
        "pointStateChanged",
        "pointDestroyed",
        "destroy"
    );
    return this ;
}
Ext.extend(Mappl.Map.Ruler.PolyLine, Ext.util.Observable, {
    getPoints : function() {
        return this.points ;
    },
    addPoint : function(aConfig) {
        var newPoint = Mappl.Map.Ruler.PointNode.classFactory.get(aConfig);
        this.getPoints().add(newPoint);
        newPoint.on("stateChanged", this.pointStateChanged, this);
        newPoint.on("destroy", this.pointDestroyed, this);
        return newPoint ;
    },
    pointStateChanged : function(aPoint) {
        this.fireEvent("pointStateChanged", this, aPoint);
    },
    pointDestroyed : function(aPoint) {
        this.fireEvent("pointDestroyed", this, aPoint);
    }
});
Mappl.Map.Ruler.PolyLine.classFactory = new Mappl.ClassFactory(Mappl.Map.Ruler.PolyLine, "polyLine");

Mappl.Map.Ruler.PolyLineCtrl = function(aConfig) {
    this.model = Mappl.Map.Ruler.PolyLine.classFactory.get(aConfig);
    this.model.on("destroy", this.eventFn_destroy, this);
    return this ;
}
Mappl.Map.Ruler.PolyLineCtrl.prototype = {
    addPoint : function(aPoint) {
        var newPoint = this.getModel().addPoint(aPoint);
        this.getModel().fireEvent("afterAddPoint", newPoint);
    },
    destroy : function(e) {
        this.getModel().fireEvent("destroy", this.getModel());
    },
    eventFn_destroy : function(e) {
        this.model.un("destroy", this.eventFn_destroy, this);
        delete this.model ;
    }
}
Mappl.Map.Ruler.PolyLineCtrl.classFactory = new Mappl.ClassFactory(Mappl.Map.Ruler.PolyLineCtrl, "polyLineCtrl");

Mappl.Map.Ruler.PolyLineView = function(aConfig) {
    if ("model" in aConfig && "ctrl" in aConfig) {
        this.model = Mappl.Map.Ruler.PolyLine.classFactory.get(aConfig.model);
        this.ctrl = Mappl.Map.Ruler.PolyLineCtrl.classFactory.get(aConfig.ctrl);
    } else {
        this.model = Mappl.Map.Ruler.PolyLine.classFactory.get(aConfig);
        this.ctrl = Mappl.Map.Ruler.PolyLineCtrl.classFactory.get(this.model);
    }
    this.points = new Array();
    if (this.getModel().getPoints().length > 0) {
        for (var i = 0, l = this.getModel().getPoints().length ; i < l ; i ++) {
            var point = this.getModel().getPoints()[i];
            this.points.add(Mappl.Map.Ruler.PolyLineView.classFactory.get(point));
        }
    }
    //console.dir(this.ctrl);
    this.dom = null ;
    this.canvas = null ;
    this.getModel().on("destroy", this.eventFn_destroy, this);
    this.getModel().on("pointDestroyed", this.eventFn_pointDestroyed, this);
    this.getModel().on("pointStateChanged", this.eventFn_pointStateChanged, this);
    return this ;
}
Mappl.Map.Ruler.PolyLineView.classFactory = new Mappl.ClassFactory(Mappl.Map.Ruler.PolyLineView, "pointLineView");
Mappl.Map.Ruler.PolyLineView.prototype = {
    getModel : function() { return this.model ; },
    getCtrl : function() { return this.ctrl ; },
    getPoints : function() {
        return this.points ;
    },
    getDom : function() {
        if (this.dom == null) this.build();
        return this.dom ;
    },
    build : function() {
        this.dom = e$("div class='mappl-map-ruler-polyline'")
                .ae$(
                    this.canvas = e$("canvas").css$({
                        width : "100%",
                        height : "100%"
                    })
                );
    },
    // Attach events after added to dom
    attach : function() {
        with (Ext.get(this.dom)) {
        }
    },
    // Detach events before remove from dom
    detach : function() {
        Ext.get(this.dom).removeAllListeners();
    },
    renderTo : function(renderToElement) {
        this._removeChild();
        renderToElement.appendChild(this.getDom());
        this.attach();
    },
    draw : function() {
        if (this.canvas.getContext) {
            var ctx = this.canvas.getContext('2d');
            if (this.getPoints().length > 1) {
                ctx.beginPath();
                var point = this.getPoints()[0].getModel();
                ctx.moveTo(point.getX(), point.getY());
                for (var i = 1, l = this.getPoints().length ; i < l ; i ++) {
                    point = this.getPoints()[i].getModel();
                    ctx.lineTo(point.getX(), point.getY());
                }
                ctx.stroke();
                ctx.closePath();
            }
        }
    },
    clear : function() {
        if (this.canvas.getContext) {
            var ctx = this.canvas.getContext('2d');
            ctx.clearRect(0, 0, Ext.get(this.canvas).getWidth(), Ext.get(this.canvas).getHeight());
        }
    },
    _removeChild : function() {
        if (this.getDom().parentNode) {
            this.detach();
            this.getDom().parentNode.removeChild(this.dom);
        }
    },
    eventFn_pointDestroyed : function(aPolyLine, aPoint) {

    },
    eventFn_pointStateChanged : function(aPolyLine, aPoint) {

    },
    getCanvas : function() {
        return this.canvas ;
    }
}
